from PySide6.QtWidgets import QMainWindow, QTableWidgetItem, QHeaderView, QFileDialog,QStyledItemDelegate,QMessageBox
from PySide6 import QtGui
from PySide6.QtCore import Qt, QTimer
from datetime import datetime
import os

from ui.datalogger_ui import Ui_DataCurve
from download import DownloadDialog

# 导入所有重构的模块
from core.event_bus import event_bus, EventType
from core.config import Config
from serial_manager import SerialManager
from data_processor import DataProcessor
from time_manager import TimeManager
from download_manager import DownloadManager


class Window(QMainWindow):
    """
    重构后的主窗口类 - 采用依赖注入和事件驱动架构
    职责:仅负责UI展示和用户交互,业务逻辑由各专职模块处理
    """
    
    def __init__(self, parent=None):
        super().__init__(parent)
        # 初始化UI
        self.ui = Ui_DataCurve()
        self.ui.setupUi(self)
        
        # 初始化下载对话框
        self.download_ui = DownloadDialog()
        # 文件名 -> 行号 的映射，用于进度更新
        self._file_row_map = {}

        # 初始化核心模块
        self._initialize_modules()
        # 初始化UI组件
        self._initialize_ui()
        # 连接UI信号
        self._connect_ui_signals()
        # 连接下载对话框信号
        self._connect_download_signals()
        # 连接事件监听
        self._connect_events()
        # 初始化定时器
        self._initialize_timers()

    def _initialize_modules(self):
        """初始化所有核心模块"""
        # 事件总线使用全局单例，无需实例化
        # 配置使用 Config 类即可，无需实例化
        # 业务模块按其真实签名创建
        self.serial_manager = SerialManager()
        self.data_processor = DataProcessor()
        self.time_manager = TimeManager(self.serial_manager)
        self.download_manager = DownloadManager(self.serial_manager)
    
    def _initialize_ui(self):
        """初始化UI组件"""
        # 表格初始化
        self._setup_table()
        # 设置按钮图标
        self._setup_button_icons()
        # 初始化ComboBox
        self._setup_combo_box()
        # 设置默认下载路径
        self._setup_default_download_path()
        # 初始化状态显示
        self.ui.label_DeviceTime.setText("设备未连接")

    def _setup_table(self):
        """设置数据表格"""
        self.ui.tableWidget.setColumnCount(3)
        self.ui.tableWidget.setRowCount(16)
        self.ui.tableWidget.setHorizontalHeaderLabels(['ID','参数名称','实际值'])
        self.ui.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.ui.tableWidget.horizontalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter)
        self.ui.tableWidget.verticalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter)
        self.ui.tableWidget.setAlternatingRowColors(True)
         # 设定第1列(ID)与第3列(实际值)不可编辑
        class _ReadOnlyDelegate(QStyledItemDelegate):
            def createEditor(self, parent, option, index):
                return None
        readonly_delegate = _ReadOnlyDelegate(self.ui.tableWidget)
        self.ui.tableWidget.setItemDelegateForColumn(0, readonly_delegate)
        self.ui.tableWidget.setItemDelegateForColumn(2, readonly_delegate)

    def _setup_button_icons(self):
        """设置按钮图标"""
        self.icon_config = Config.get_icon_config()
        self.ui.btnRefresh.setIcon(self._create_icon(self.icon_config['refresh']))
        self.ui.startbtn.setIcon(self._create_icon(self.icon_config['start']))
        self.ui.realbtn.setIcon(self._create_icon(self.icon_config['real']))
        self.ui.btnInterval.setIcon(self._create_icon(self.icon_config['save']))
        self.ui.btnCaliTime.setIcon(self._create_icon(self.icon_config['time']))
        self.ui.btnFile.setIcon(self._create_icon(self.icon_config['path']))
        self.ui.btnDownload.setIcon(self._create_icon(self.icon_config['download']))
        self.ui.btnSend.setIcon(self._create_icon(self.icon_config['config']))

    def _create_icon(self, icon_path):
        """创建图标"""
        ico = QtGui.QPixmap(icon_path)
        return QtGui.QIcon(ico)

    def _setup_combo_box(self):
        """设置采集间隔下拉框"""
        self.ui.comboBox.clear()
        self.ui.comboBox.addItems(Config.INTERVAL_OPTIONS)
        self.ui.comboBox.currentTextChanged.connect(self._on_interval_changed)

    def _setup_default_download_path(self):
        """设置默认下载路径"""
        default_path = os.path.join(os.path.expanduser("~"), "Desktop")
        self.ui.lineEdit.setText(default_path)
        self.ui.lineEdit.setReadOnly(True)

    def _connect_events(self):
        """连接模块事件监听（基于事件总线订阅）"""
        # 设备事件
        # event_bus.subscribe(EventType.DEVICE_CONNECTED, self._on_device_connected)
        event_bus.subscribe(EventType.DEVICE_DISCONNECTED, self._on_device_disconnected)
        # 数据/时间事件 
        event_bus.subscribe(EventType.SENSOR_DATA_UPDATED, self._on_sensor_data_updated)
        event_bus.subscribe(EventType.DEVICE_TIME_UPDATED, self._on_device_time_updated)
        event_bus.subscribe(EventType.DEVICE_SAMPLING_INTERVAL, self._on_sampling_interval_updated)
        # 下载事件
        event_bus.subscribe(EventType.FILE_LIST_RECEIVED, self._on_file_list_updated)
        event_bus.subscribe(EventType.DOWNLOAD_PROGRESS, self._on_download_progress_updated)
        event_bus.subscribe(EventType.DOWNLOAD_COMPLETE, self._on_download_complete)
        # 配置事件
        event_bus.subscribe(EventType.CONFIG_HEADER_UPDATED, self._on_config_header_updated)

    def _connect_ui_signals(self):
        """连接UI信号槽"""
        self.ui.btnRefresh.clicked.connect(self._on_refresh_ports)
        self.ui.startbtn.clicked.connect(self._on_connect_device)
        self.ui.btnInterval.clicked.connect(self._on_set_logging_interval)
        self.ui.btnCaliTime.clicked.connect(self._on_calibrate_time)
        self.ui.btnDownload.clicked.connect(self._on_download_data)
        self.ui.btnFile.clicked.connect(self._on_choose_folder)
        self.ui.realbtn.clicked.connect(self._on_real_data)
        self.ui.btnSend.clicked.connect(self._on_send_config_header)

    def _connect_download_signals(self):
        """连接下载对话框信号"""
        self.download_ui.downloadRequested.connect(self._on_download_requested)
        self.download_ui.deleteRequested.connect(self._on_delete_requested)
        self.download_ui.downloadConfirmed.connect(self._on_download_confirmed)
        self.download_ui.downloadCanceled.connect(self._on_download_canceled)
        self.download_ui.dialogClosed.connect(self._on_dialog_closed)

    def _initialize_timers(self):
        """初始化定时器"""
        self.timer = QTimer(self)
        self.timer.timeout.connect(self._update_local_time)
        self.timer.start(1000)
        self._update_local_time()

    def show_message(self):
        # QMessageBox.about(self,'消息对话框','设置成功!')
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle('消息对话框')
        msg_box.setText('设置成功!')
        # msg_box.setIcon(QMessageBox.NoIcon)  # 无图标
        custom_icon = QtGui.QIcon('ico/message.png')
        msg_box.setIconPixmap(custom_icon.pixmap(25,25)) 
        msg_box.setStyleSheet("font-size: 12px;font-weight: bold;")
        msg_box.exec()

    # UI事件处理方法
    def _on_refresh_ports(self):
        """刷新串口列表"""
        ports = self.serial_manager.get_available_ports()
        self.ui.cbbPortNum.clear()
        self.ui.cbbPortNum.addItems(ports)

    def _on_connect_device(self):
        """连接/断开设备（单一反转按钮）"""
        port = self.ui.cbbPortNum.currentText()
        # 已连接 -> 断开
        if self.serial_manager.is_connected:
            # 通知设备断开
            event_bus.emit(EventType.DEVICE_DISCONNECTED)
            # 停止接收并断开串口
            self.serial_manager.stop_receiving()
            self.serial_manager.disconnect()
            # 可选：更新按钮文本/图标
            self.ui.startbtn.setText("连接")
            self.ui.startbtn.setIcon(self._create_icon(self.icon_config['start']))
            return

        # 未连接 -> 连接
        if not port:
            print("未选择串口,无法连接设备")
            return
        if not self.serial_manager.connect(port):
            print("串口连接失败")
            return

        started = self.serial_manager.start_receiving()
        if started:
            # 请求设备时间
            self.serial_manager.send_data(Config.CMD_TYPES['GET_TIME'])
           # 可选：更新按钮文本/图标
            self.ui.startbtn.setText("断开")
            self.ui.startbtn.setIcon(self._create_icon(self.icon_config['pause']))

    def _on_set_logging_interval(self):
        """设置采集间隔"""

        interval_text = self.ui.comboBox.currentText()
        interval_int =self.data_processor._parse_interval_time(interval_text)
        if not self.serial_manager.is_connected:
            print("未连接设备，无法设置采集间隔")
            return

        success = self.serial_manager.send_data(
            Config.CMD_TYPES['SET_INTERVAL'],
            Config.CTRL_MSG_TYPES['CONTROL_INPUT'],
            interval_int,
            control_id='0012'
        )
        if success:
            self.show_message()

    def _on_calibrate_time(self):
        """校准设备时间"""
        success = self.time_manager.sync_time_with_device()
        if not success:
            print("时间校准失败：设备未连接")
            return
        self.show_message()

    def _on_download_data(self):
        """开始下载数据：确保串口接收并请求文件列表"""
        try:
            # 1) 设置并同步下载路径
            folder = (self.ui.lineEdit.text() or "").strip()
            if not folder:
                folder = Config.get_default_download_path()
                self.ui.lineEdit.setText(folder)
            self.download_manager.set_download_path(folder)

            # 2) 确保串口连接
            if not (self.serial_manager.is_connected and self.serial_manager.is_receiving):
                port = self.ui.cbbPortNum.currentText()
                if not port:
                    print("未选择串口，无法下载数据")
                    return
                if not self.serial_manager.connect(port):
                    print("串口连接失败，无法下载数据")
                    return

            # 4) 重置对话框状态与进度映射，并初始化表格
            self._file_row_map.clear()
            if hasattr(self.download_ui, "last_len"):
                self.download_ui.last_len = 0
            if hasattr(self.download_ui, "files_list"):
                self.download_ui.files_list = []
            self.download_ui.dialog_table_init()

            # 5) 请求文件列表并打开对话框
            ok = self.download_manager.get_file_list()
            if not ok:
                print("请求文件列表失败：设备未连接或指令发送失败")
                return

            # 打开为模态对话框
            self.download_ui.dialog.exec()
        except Exception as e:
            print(f"_on_download_data 错误: {e}")

    def _on_choose_folder(self):
        """选择下载文件夹"""
        folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder:
            self.ui.lineEdit.setText(folder)
            self.download_manager.set_download_path(folder)

    def _on_real_data(self):
        """请求实时数据"""
        if not self.serial_manager.is_connected:
            print("未连接设备，无法请求实时数据")
            return
        self.download_manager.stop_download()
        self.serial_manager.send_data(Config.CMD_TYPES['REAL_DATA'])

    def _on_interval_changed(self):
        """采集间隔改变时恢复按钮文本"""
        self.ui.btnInterval.setText("保存间隔")

    # 下载对话框事件处理
    def _on_download_requested(self, file_name, row):
        """处理下载请求"""
        self._file_row_map[file_name] = row
        # 禁用表格
        self.download_ui.dl_dialog.tableWidget.setEnabled(False)
       
        ok = self.download_manager.download_file(file_name)
        if ok:
            # 可选：在进度列先显示“等待开始”
            try:
                self.download_ui.dl_dialog.tableWidget.setItem(row, 2, QTableWidgetItem("等待开始"))
            except Exception:
                pass
        else:
            print(f"[download] 启动下载失败: {file_name} (row={row})，可能是串口未连接或已有文件正在下载")
            # 失败时恢复可点击
            try:
                self.download_ui.dl_dialog.tableWidget.setEnabled(True)
            except Exception:
                pass

    def _on_delete_requested(self, file_name, row):
        """处理删除请求"""
        self._file_row_map[file_name] = row
        self.download_manager.delete_file(file_name)
        self.download_ui.dl_dialog.tableWidget.removeRow(row)
        
    def _on_download_confirmed(self):
        """下载确认"""
        self.download_manager.stop_download()

    def _on_download_canceled(self):
        """下载取消"""
        self.download_manager.stop_download()

    def _on_dialog_closed(self):
        """对话框关闭"""
        self.download_manager.stop_download()


    def _on_serial_error(self, error_msg):
        """串口错误处理"""
        print(f"串口错误: {error_msg}")

    def _on_device_disconnected(self,data=None):    
        """设备断开连接"""
        self.serial_manager.send_data(Config.CMD_TYPES['STOP_TIME'])
        self.ui.label_DeviceTime.setText("设备未连接")
        self.ui.tableWidget.clearContents()

    def _on_send_config_header(self):
        """发送配置头"""
        names = []
        total_rows = self.ui.tableWidget.rowCount()
        for r in range(total_rows):
            item = self.ui.tableWidget.item(r, 1)
            text = "" if item is None else (item.text() or "").strip()
            names.append(text)
        header = {i + 1: name for i, name in enumerate(names)}
        print(header)
        success =  self.serial_manager.send_data(Config.CMD_TYPES['SET_INTERVAL'],
                                    Config.CTRL_MSG_TYPES['CONTROL_INPUT'], 
                                    header,
                                    control_id='0015')
        if success:
            self.show_message()

    def _on_sensor_data_updated(self, payload):
        """更新传感器数据显示"""
        try:
            self._update_table_data(
                payload['sensor_id'],
                payload['water_content'],
                payload['temperature'],
                payload['power_voltage']
            )
        except Exception as e:
            print(f"_on_sensor_data_updated 错误: {e}")


    def _on_device_time_updated(self, payload):                                 
        """设备时间显示"""
        try:
            device_time = payload.get('device_time')
            if hasattr(device_time, 'strftime'):
                self.ui.label_DeviceTime.setText(device_time.strftime("%Y-%m-%d %H:%M:%S"))
            else:
                self.ui.label_DeviceTime.setText(str(device_time))
        except Exception as e:
            print(f"_on_device_time_updated 错误: {e}")

    def _on_sampling_interval_updated(self,payload):
        """处理来自 DataProcessor 的采样间隔更新"""
        try:
            if isinstance(payload, dict) and payload.get('type') == 'interval_update':
                interval_text = payload.get('text')
                if interval_text:
                    index = self.ui.comboBox.findText(interval_text)
                    if index >= 0:
                        self.ui.comboBox.setCurrentIndex(index)
        except Exception as e:
            print(f"_on_serial_event_for_interval 错误: {e}")
            

    def _on_file_list_updated(self, file_list):
        """更新文件列表显示"""
        self.download_ui.fileListArrived.emit(file_list)
        
       
    def _on_download_progress_updated(self, payload):
        """更新下载进度显示"""
        try:
            file_name = payload.get('file')
            progress = payload.get('progress', 0)
            row = self._file_row_map.get(file_name)
            if row is not None:
                # 通过信号在主线程里更新单元格
                self.download_ui.updateProgress.emit(row, f"{progress}%")
             
        except Exception as e:
            print(f"_on_download_progress_updated 错误: {e}")

    def _on_download_complete(self, payload):
        """下载完成"""
        try:
            file_name = payload.get('file')
            success = payload.get('success', False)
            row = self._file_row_map.get(file_name)
            if row is not None:
                final_text = "100%" if success else "失败"
                # 通过信号在主线程里更新单元格
                self.download_ui.updateProgress.emit(row, final_text)
                
              
        except Exception as e:
            print(f"_on_download_complete 错误: {e}")
        finally:
            # 下载完成后恢复可点击
            try:
                self.download_ui.dl_dialog.tableWidget.setEnabled(True)
                self.download_ui.dl_dialog.tableWidget.setItem(row, 2, QTableWidgetItem("下载完成"))
            except Exception:
                pass
        
    # 辅助方法
    def _update_local_time(self):
        """更新本地时间显示"""
        current_time = datetime.now()
        time_str = current_time.strftime("%Y-%m-%d %a %H:%M:%S")
        self.ui.label_localTime.setText(time_str)
        # 设备时间（如果已同步/连接正常则“走动”显示）
        device_time = self.time_manager.get_current_device_time() if hasattr(self, 'time_manager') else None
        if device_time:
            self.ui.label_DeviceTime.setText(device_time.strftime("%Y-%m-%d %H:%M:%S"))

    # def _on_config_header_updated(self, header: dict):
    #     """根据设备发送的 cfg header 更新参数名称列(第一列),按1..6顺序逐行写入"""
    #     try:
    #         if not isinstance(header, dict):
    #             return
    #         total_rows = self.ui.tableWidget.rowCount()
    #         for r in range(total_rows):
    #             existing = self.ui.tableWidget.item(r, 1)
    #             if existing:
    #                 existing.setText("")
    #             else:
    #                 self.ui.tableWidget.setItem(r, 1, QTableWidgetItem(""))
        
    #         row = 0
    #         # 逐一遍历传入的字典，按出现顺序写入
    #         for _, section in header.items():
    #             if not isinstance(section, dict):
    #                 continue
    #             # 按 section 的插入顺序遍历所有 param_* 键
    #             for k, v in section.items():
    #                 if not str(k).startswith("param_"):  # 仅处理以 'param_' 开头的键，其他键忽略
    #                     continue
    #                 text = "" if v is None else str(v).strip()
    #                 while row >= self.ui.tableWidget.rowCount():
    #                         self.ui.tableWidget.insertRow(self.ui.tableWidget.rowCount())

    #                 item = QTableWidgetItem(text)
    #                 item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
    #                 self.ui.tableWidget.setItem(row, 1, item)

    #                 row += 1
    #     except Exception as e:
    #         print(f"配置表头更新错误: {e}")
    def _on_config_header_updated(self, header: dict):
    # 根据设备发送的 cfg header 更新参数名称列(第一列)，兼容顺序：
    # 1) 若有 header_list（数组），按数组顺序写入；
    # 2) 否则对字典的分区键（数字优先）与 param_* 键（按后缀数字）排序写入。
        try:
            if not isinstance(header, dict):
                return

            # 预清空第1列文本
            total_rows = self.ui.tableWidget.rowCount()
            for r in range(total_rows):
                existing = self.ui.tableWidget.item(r, 1)
                if existing:
                    existing.setText("")
                else:
                    self.ui.tableWidget.setItem(r, 1, QTableWidgetItem(""))

            # 兼容多种负载形态：{"header_list": [...], "header": {...}} 或直接 {...}
            data = header
            header_dict = data.get('header') if isinstance(data.get('header'), dict) else (data if isinstance(data, dict) else {})
        
            row = 0
            def _sort_key(k):
                try:
                    return (0, int(k))
                except Exception:
                    return (1, str(k))

            def _param_index(p):
                try:
                    return int(str(p).split('_', 1)[1])
                except Exception:
                    return 999999

            for _, section in sorted(header_dict.items(), key=lambda kv: _sort_key(kv[0])):
                if not isinstance(section, dict):
                    continue
                # param_* 键按后缀数字排序
                param_keys = [k for k in section.keys() if str(k).startswith('param_')]
                param_keys.sort(key=_param_index)
                for pk in param_keys:
                    v = section.get(pk)
                    s = "" if v is None else str(v).strip()
                    while row >= self.ui.tableWidget.rowCount():
                        self.ui.tableWidget.insertRow(self.ui.tableWidget.rowCount())
                    item = QTableWidgetItem(s)
                    item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
                    self.ui.tableWidget.setItem(row, 1, item)
                    row += 1
        except Exception as e:
            print(f"配置表头更新错误: {e}")
    

    def _update_table_data(self, sensor_id, water_content, temperature, power_voltage):
        """更新表格数据显示"""
        try:
            row_index = (sensor_id - 1) * 2
            
            # 水分数据行
            id_item = QTableWidgetItem(f'No.{sensor_id}')
            water_item = QTableWidgetItem(f"{water_content:.2f}")
            self.ui.tableWidget.setItem(row_index, 0, id_item)
            self.ui.tableWidget.setItem(row_index, 2, water_item)
            
            # 温度数据行
            temp_item = QTableWidgetItem(f"{temperature:.2f}")
            self.ui.tableWidget.setItem(row_index + 1, 2, temp_item)
            
            # 电压数据行
            voltage_item = QTableWidgetItem("power voltage")
            power_item = QTableWidgetItem(f"{power_voltage:.3f}")
            self.ui.tableWidget.setItem(row_index + 2, 0, voltage_item)
            self.ui.tableWidget.setItem(row_index + 2, 2, power_item)
            
        except Exception as e:
            print(f"表格更新错误: {e}")

    def closeEvent(self, event):
        """窗口关闭事件"""
        if self.serial_manager.is_connected:
            event_bus.emit(EventType.DEVICE_DISCONNECTED)
            self.serial_manager.stop_receiving()
            self.serial_manager.disconnect()
        event.accept()

    